import $ from 'jquery';
import Button from './Button';

function array_merge(arr1, arr2) {
    arr1.push.apply(arr1, arr2);
    return arr1;
}
export default class TableButton extends Button{

  _init() {
    super._init.call(this);
    array_merge(this.editor.formatter._allowedTags, ['thead', 'th', 'tbody', 'tr', 'td', 'colgroup', 'col']);
    $.extend(this.editor.formatter._allowedAttributes, {
      td: ['rowspan', 'colspan'],
      col: ['width']
    });
    $.extend(this.editor.formatter._allowedStyles, {
      td: ['text-align'],
      th: ['text-align']
    });
    this._initShortcuts();
    this.editor.on('decorate', (e, $el) => $el.find('table').each((i, table) => this.decorate($(table))));
    this.editor.on('undecorate', (e, $el) => $el.find('table').each((i, table) => this.undecorate($(table))));
    this.editor.on('selectionchanged.table', ((_this => e => {
      let $container;
      let range;
      _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active');
      range = _this.editor.selection.range();
      if (!range) {
        return;
      }
      $container = _this.editor.selection.containerNode();
      if (range.collapsed && $container.is('.simditor-table')) {
        if (_this.editor.selection.rangeAtStartOf($container)) {
          $container = $container.find('th:first');
        } else {
          $container = $container.find('td:last');
        }
        _this.editor.selection.setRangeAtEndOf($container);
      }
      return $container.closest('td, th', _this.editor.body).addClass('active');
    }))(this));
    this.editor.on('blur.table', ((_this => e => _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active')))(this));
    this.editor.keystroke.add('up', 'td', (e, $node) => {
      this._tdNav($node, 'up');
      return true;
    });
    this.editor.keystroke.add('up', 'th',(e, $node) => {
      this._tdNav($node, 'up');
      return true;
    });
    this.editor.keystroke.add('down', 'td', (e, $node) => {
      this._tdNav($node, 'down');
      return true;
    });
    this.editor.keystroke.add('down', 'th', (e, $node) => {
      this._tdNav($node, 'down');
      return true;
    });
  }

  _tdNav($td, direction) {
    let $anotherTr, $tr, action, index;
    if (direction == null) direction = 'up';
    
    action = direction === 'up' ? 'prev' : 'next';
    direction === 'up' ? ['tbody', 'thead'] : ['thead', 'tbody'];
    $tr = $td.parent('tr');
    $anotherTr = this[`_${action}Row`]($tr);
    if (!($anotherTr.length > 0)) return true;
    index = $tr.find('td, th').index($td);
    this.editor.selection.setRangeAtEndOf($anotherTr.find('td, th').eq(index));
  }

  _nextRow($tr) {
    let $nextTr;
    $nextTr = $tr.next('tr');
    if ($nextTr.length < 1 && $tr.parent('thead').length > 0) {
      $nextTr = $tr.parent('thead').next('tbody').find('tr:first');
    }
    return $nextTr;
  }

  _prevRow($tr) {
    let $prevTr;
    $prevTr = $tr.prev('tr');
    if ($prevTr.length < 1 && $tr.parent('tbody').length > 0) {
      $prevTr = $tr.parent('tbody').prev('thead').find('tr');
    }
    return $prevTr;
  }

  initResize($table) {
    let $colgroup;
    let $resizeHandle;
    let $wrapper;
    $wrapper = $table.parent('.simditor-table');
    $colgroup = $table.find('colgroup');
    if ($colgroup.length < 1) {
      $colgroup = $('<colgroup/>').prependTo($table);
      $table.find('thead tr th').each(() => {
        return $('<col/>').appendTo($colgroup);
      });
      this.refreshTableWidth($table);
    }
    $resizeHandle = $('<div />', {
      "class": 'simditor-resize-handle',
      contenteditable: 'false'
    }).appendTo($wrapper);
    $wrapper.on('mousemove', 'td, th', e => {
      let $col;
      let $td;
      let index;
      let ref;
      let ref1;
      let x;
      if ($wrapper.hasClass('resizing')) {
        return;
      }
      $td = $(e.currentTarget);
      x = e.pageX - $(e.currentTarget).offset().left;
      if (x < 5 && $td.prev().length > 0) {
        $td = $td.prev();
      }
      if ($td.next('td, th').length < 1) {
        $resizeHandle.hide();
        return;
      }
      if ((ref = $resizeHandle.data('td')) != null ? ref.is($td) : void 0) {
        $resizeHandle.show();
        return;
      }
      index = $td.parent().find('td, th').index($td);
      $col = $colgroup.find('col').eq(index);
      if ((ref1 = $resizeHandle.data('col')) != null ? ref1.is($col) : void 0) {
        $resizeHandle.show();
        return;
      }
      return $resizeHandle.css('left', $td.position().left + $td.outerWidth() - 5).data('td', $td).data('col', $col).show();
    });
    $wrapper.on('mouseleave', e => $resizeHandle.hide());
    return $wrapper.on('mousedown', '.simditor-resize-handle', e => {
      let $handle;
      let $leftCol;
      let $leftTd;
      let $rightCol;
      let $rightTd;
      let minWidth;
      let startHandleLeft;
      let startLeftWidth;
      let startRightWidth;
      let startX;
      let tableWidth;
      $handle = $(e.currentTarget);
      $leftTd = $handle.data('td');
      $leftCol = $handle.data('col');
      $rightTd = $leftTd.next('td, th');
      $rightCol = $leftCol.next('col');
      startX = e.pageX;
      startLeftWidth = $leftTd.outerWidth() * 1;
      startRightWidth = $rightTd.outerWidth() * 1;
      startHandleLeft = parseFloat($handle.css('left'));
      tableWidth = $leftTd.closest('table').width();
      minWidth = 50;
      $(document).on('mousemove.simditor-resize-table', e => {
        let deltaX;
        let leftWidth;
        let rightWidth;
        deltaX = e.pageX - startX;
        leftWidth = startLeftWidth + deltaX;
        rightWidth = startRightWidth - deltaX;
        if (leftWidth < minWidth) {
          leftWidth = minWidth;
          deltaX = minWidth - startLeftWidth;
          rightWidth = startRightWidth - deltaX;
        } else if (rightWidth < minWidth) {
          rightWidth = minWidth;
          deltaX = startRightWidth - minWidth;
          leftWidth = startLeftWidth + deltaX;
        }
        $leftCol.attr('width', `${leftWidth / tableWidth * 100}%`);
        $rightCol.attr('width', `${rightWidth / tableWidth * 100}%`);
        return $handle.css('left', startHandleLeft + deltaX);
      });
      $(document).one('mouseup.simditor-resize-table', e => {
        $(document).off('.simditor-resize-table');
        return $wrapper.removeClass('resizing');
      });
      $wrapper.addClass('resizing');
      return false;
    });
  }

  _initShortcuts() {
    this.editor.hotkeys.add('ctrl+alt+up', ((_this => e => {
      _this.editMenu.find('.menu-item[data-param=insertRowAbove]').click();
      return false;
    }))(this));
    this.editor.hotkeys.add('ctrl+alt+down', ((_this => e => {
      _this.editMenu.find('.menu-item[data-param=insertRowBelow]').click();
      return false;
    }))(this));
    this.editor.hotkeys.add('ctrl+alt+left', ((_this => e => {
      _this.editMenu.find('.menu-item[data-param=insertColLeft]').click();
      return false;
    }))(this));
    return this.editor.hotkeys.add('ctrl+alt+right', ((_this => e => {
      _this.editMenu.find('.menu-item[data-param=insertColRight]').click();
      return false;
    }))(this));
  }

  decorate($table) {
    let $headRow;
    let $tbody;
    let $thead;
    if ($table.parent('.simditor-table').length > 0) {
      this.undecorate($table);
    }
    $table.wrap('<div class="simditor-table"></div>');
    if ($table.find('thead').length < 1) {
      $thead = $('<thead />');
      $headRow = $table.find('tr').first();
      $thead.append($headRow);
      this._changeCellTag($headRow, 'th');
      $tbody = $table.find('tbody');
      if ($tbody.length > 0) {
        $tbody.before($thead);
      } else {
        $table.prepend($thead);
      }
    }
    this.initResize($table);
    return $table.parent();
  }

  undecorate($table) {
    if (!($table.parent('.simditor-table').length > 0)) {
      return;
    }
    return $table.parent().replaceWith($table);
  }

  renderMenu() {
    let $table;
    $(`<div class="menu-create-table">\n</div>\n<div class="menu-edit-table">\n  <ul>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="deleteRow">\n        <span>${this._t('deleteRow')}</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="insertRowAbove">\n        <span>${this._t('insertRowAbove')} ( Ctrl + Alt + ↑ )</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="insertRowBelow">\n        <span>${this._t('insertRowBelow')} ( Ctrl + Alt + ↓ )</span>\n      </a>\n    </li>\n    <li><span class="separator"></span></li>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="deleteCol">\n        <span>${this._t('deleteColumn')}</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="insertColLeft">\n        <span>${this._t('insertColumnLeft')} ( Ctrl + Alt + ← )</span>\n      </a>\n    </li>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="insertColRight">\n        <span>${this._t('insertColumnRight')} ( Ctrl + Alt + → )</span>\n      </a>\n    </li>\n    <li><span class="separator"></span></li>\n    <li>\n      <a tabindex="-1" unselectable="on" class="menu-item"\n        href="javascript:;" data-param="deleteTable">\n        <span>${this._t('deleteTable')}</span>\n      </a>\n    </li>\n  </ul>\n</div>`).appendTo(this.menuWrapper);
    this.createMenu = this.menuWrapper.find('.menu-create-table');
    this.editMenu = this.menuWrapper.find('.menu-edit-table');
    $table = this.createTable(6, 6).appendTo(this.createMenu);
    this.createMenu.on('mouseenter', 'td, th', ((_this => e => {
      let $td;
      let $tr;
      let $trs;
      let num;
      _this.createMenu.find('td, th').removeClass('selected');
      $td = $(e.currentTarget);
      $tr = $td.parent();
      num = $tr.find('td, th').index($td) + 1;
      $trs = $tr.prevAll('tr').addBack();
      if ($tr.parent().is('tbody')) {
        $trs = $trs.add($table.find('thead tr'));
      }
      return $trs.find(`td:lt(${num}), th:lt(${num})`).addClass('selected');
    }))(this));
    this.createMenu.on('mouseleave', e => $(e.currentTarget).find('td, th').removeClass('selected'));
    return this.createMenu.on('mousedown', 'td, th', ((_this => e => {
      let $closestBlock;
      let $td;
      let $tr;
      let colNum;
      let rowNum;
      _this.wrapper.removeClass('menu-on');
      if (!_this.editor.inputManager.focused) {
        return;
      }
      $td = $(e.currentTarget);
      $tr = $td.parent();
      colNum = $tr.find('td').index($td) + 1;
      rowNum = $tr.prevAll('tr').length + 1;
      if ($tr.parent().is('tbody')) {
        rowNum += 1;
      }
      $table = _this.createTable(rowNum, colNum, true);
      $closestBlock = _this.editor.selection.blockNodes().last();
      if (_this.editor.util.isEmptyNode($closestBlock)) {
        $closestBlock.replaceWith($table);
      } else {
        $closestBlock.after($table);
      }
      _this.decorate($table);
      _this.editor.selection.setRangeAtStartOf($table.find('th:first'));
      _this.editor.trigger('valuechanged');
      return false;
    }))(this));
  }

  createTable(row, col, phBr) {
    let $table;
    let $tbody;
    let $td;
    let $thead;
    let $tr;
    let k;
    let l;
    let r;
    let ref;
    let ref1;
    $table = $('<table/>');
    $thead = $('<thead/>').appendTo($table);
    $tbody = $('<tbody/>').appendTo($table);
    for (r = k = 0, ref = row; 0 <= ref ? k < ref : k > ref; r = 0 <= ref ? ++k : --k) {
      $tr = $('<tr/>');
      $tr.appendTo(r === 0 ? $thead : $tbody);
      for (l = 0, ref1 = col; 0 <= ref1 ? l < ref1 : l > ref1; 0 <= ref1 ? ++l : --l) {
        $td = $(r === 0 ? '<th/>' : '<td/>').appendTo($tr);
        if (phBr) {
          $td.append(this.editor.util.phBr);
        }
      }
    }
    return $table;
  }

  refreshTableWidth($table) {
    let cols;
    let tableWidth;
    tableWidth = $table.width();
    cols = $table.find('col');
    return $table.find('thead tr th').each((i, td) => {
      let $col;
      $col = cols.eq(i);
      return $col.attr('width', `${$(td).outerWidth() / tableWidth * 100}%`);
    });
  }

  setActive(active) {
    super.setActive.call(this, active);
    if (active) {
      this.createMenu.hide();
      return this.editMenu.show();
    } else {
      this.createMenu.show();
      return this.editMenu.hide();
    }
  }

  _changeCellTag($tr, tagName) {
    return $tr.find('td, th').each((i, cell) => {
      let $cell;
      $cell = $(cell);
      return $cell.replaceWith(`<${tagName}>${$cell.html()}</${tagName}>`);
    });
  }

  deleteRow($td) {
    let $newTr;
    let $tr;
    let index;
    $tr = $td.parent('tr');
    if ($tr.closest('table').find('tr').length < 1) {
      return this.deleteTable($td);
    } else {
      $newTr = this._nextRow($tr);
      if (!($newTr.length > 0)) {
        $newTr = this._prevRow($tr);
      }
      index = $tr.find('td, th').index($td);
      if ($tr.parent().is('thead')) {
        $newTr.appendTo($tr.parent());
        this._changeCellTag($newTr, 'th');
      }
      $tr.remove();
      return this.editor.selection.setRangeAtEndOf($newTr.find('td, th').eq(index));
    }
  }

  insertRow($td, direction) {
    let $newTr;
    let $table;
    let $tr;
    let cellTag;
    let colNum;
    let index;
    let k;
    let ref;
    if (direction == null) {
      direction = 'after';
    }
    $tr = $td.parent('tr');
    $table = $tr.closest('table');
    colNum = 0;
    $table.find('tr').each((i, tr) => colNum = Math.max(colNum, $(tr).find('td').length));
    index = $tr.find('td, th').index($td);
    $newTr = $('<tr/>');
    cellTag = 'td';
    if (direction === 'after' && $tr.parent().is('thead')) {
      $tr.parent().next('tbody').prepend($newTr);
    } else if (direction === 'before' && $tr.parent().is('thead')) {
      $tr.before($newTr);
      $tr.parent().next('tbody').prepend($tr);
      this._changeCellTag($tr, 'td');
      cellTag = 'th';
    } else {
      $tr[direction]($newTr);
    }
    for (k = 1, ref = colNum; 1 <= ref ? k <= ref : k >= ref; 1 <= ref ? ++k : --k) {
      $(`<${cellTag}/>`).append(this.editor.util.phBr).appendTo($newTr);
    }
    return this.editor.selection.setRangeAtStartOf($newTr.find('td, th').eq(index));
  }

  deleteCol($td) {
    let $newTd;
    let $table;
    let $tr;
    let index;
    let noOtherCol;
    let noOtherRow;
    $tr = $td.parent('tr');
    noOtherRow = $tr.closest('table').find('tr').length < 2;
    noOtherCol = $td.siblings('td, th').length < 1;
    if (noOtherRow && noOtherCol) {
      return this.deleteTable($td);
    } else {
      index = $tr.find('td, th').index($td);
      $newTd = $td.next('td, th');
      if (!($newTd.length > 0)) {
        $newTd = $tr.prev('td, th');
      }
      $table = $tr.closest('table');
      $table.find('col').eq(index).remove();
      $table.find('tr').each((i, tr) => $(tr).find('td, th').eq(index).remove());
      this.refreshTableWidth($table);
      return this.editor.selection.setRangeAtEndOf($newTd);
    }
  }

  insertCol($td, direction) {
    let $col;
    let $newCol;
    let $newTd;
    let $table;
    let $tr;
    let index;
    let tableWidth;
    let width;
    if (direction == null) {
      direction = 'after';
    }
    $tr = $td.parent('tr');
    index = $tr.find('td, th').index($td);
    $table = $td.closest('table');
    $col = $table.find('col').eq(index);
    $table.find('tr').each(((_this => (i, tr) => {
      let $newTd;
      let cellTag;
      cellTag = $(tr).parent().is('thead') ? 'th' : 'td';
      $newTd = $(`<${cellTag}/>`).append(_this.editor.util.phBr);
      return $(tr).find('td, th').eq(index)[direction]($newTd);
    }))(this));
    $newCol = $('<col/>');
    $col[direction]($newCol);
    tableWidth = $table.width();
    width = Math.max(parseFloat($col.attr('width')) / 2, 50 / tableWidth * 100);
    $col.attr('width', `${width}%`);
    $newCol.attr('width', `${width}%`);
    this.refreshTableWidth($table);
    $newTd = direction === 'after' ? $td.next('td, th') : $td.prev('td, th');
    return this.editor.selection.setRangeAtStartOf($newTd);
  }

  deleteTable($td) {
    let $block;
    let $table;
    $table = $td.closest('.simditor-table');
    $block = $table.next('p');
    $table.remove();
    if ($block.length > 0) {
      return this.editor.selection.setRangeAtStartOf($block);
    }
  }

  command(param) {
    let $td;
    $td = this.editor.selection.containerNode().closest('td, th');
    if (!($td.length > 0)) {
      return;
    }
    if (param === 'deleteRow') {
      this.deleteRow($td);
    } else if (param === 'insertRowAbove') {
      this.insertRow($td, 'before');
    } else if (param === 'insertRowBelow') {
      this.insertRow($td);
    } else if (param === 'deleteCol') {
      this.deleteCol($td);
    } else if (param === 'insertColLeft') {
      this.insertCol($td, 'before');
    } else if (param === 'insertColRight') {
      this.insertCol($td);
    } else if (param === 'deleteTable') {
      this.deleteTable($td);
    } else {
      return;
    }
    return this.editor.trigger('valuechanged');
  }
}

TableButton.prototype.name = 'table';
TableButton.prototype.icon = 'table';
TableButton.prototype.htmlTag = 'table';
TableButton.prototype.disableTag = 'pre, li, blockquote';
TableButton.prototype.menu = true;